#if UNITY_EDITOR && DEBUG_BEHAVIOUR_TREE
#define DEBUG_TREE
#endif
using System.Collections.Generic;
using UnityEngine;

namespace GameToolkit.BehaviourTree
{
    public sealed class BehaviourTreeLooper //: IBehaviourTreeLooper
    {
        BehaviourTreePlayer mPlayer;
        ExecutableEntry mRoot;
        ExecutableEntry[] mExecutionBuffer;
        int mExecutionCount;
        // 安全检查：为了防止某些控制节点在返回下一次执行节点时错误返回已经执行过的不可执行的节点，进而导致死锁，安全检查将排除这种重复执行的情况，从而确保程序不会发生死锁现象。
        HashSet<ExecutableEntry> mSaftyCheck;
        IExecutableRootCallback[] mRootCallbacks;
        NodeState mState;
        public NodeState State => mState;
        public ExecutableEntry Root => mRoot;
        public ExecutableEntry ActiveNode => mExecutionCount > 0 ? mExecutionBuffer[mExecutionCount - 1] : null;

        internal BehaviourTreeLooper(BehaviourTreePlayer player, ExecutableEntry root, bool isMainLooper)
        {
            mPlayer = player;
            mRoot = root;
            int maxDepth = BehaviourTreePlayer.GetTreeDepth(root, false);
            mExecutionBuffer = new ExecutableEntry[maxDepth];
            mSaftyCheck = new HashSet<ExecutableEntry>();
            if ( maxDepth > 0 && isMainLooper)
            {
                var rootcall = new List<IExecutableRootCallback>(maxDepth << 1);
                BehaviourTreePlayer.InvokeRecursive(mRoot, true, (it) => it.GetRootCallbacks(rootcall));
                mRootCallbacks = rootcall.ToArray();
            }
            else
            {
                mRootCallbacks = new IExecutableRootCallback[0];
            }

        }

        public T GetActiveNode<T>() where T : class
        {
            for (int i = 0; i < mExecutionCount; i++)
            {
                if (mExecutionBuffer[i] is T t)
                    return t;
            }
            return default;
        }

        public void Interrupt(bool resetState, bool successInterrupt)
        {
            if (mExecutionCount > 0)
            {
                // interrupt
                for (int lastIndex = mExecutionCount - 1; lastIndex >= 0; lastIndex--)
                {
                    var last = mExecutionBuffer[lastIndex];
                    last.InvokeInterrupt(mPlayer, successInterrupt);
                    last.InvokeOnStop(mPlayer);
                }
                mExecutionCount = 0;
                mState = resetState ? NodeState.Inactive : mRoot.State;
            }
            else if (resetState)
            {
                mState = NodeState.Inactive;
            }
        }

        public void UpdateBehaviour()
        {
            // start behaviuor tree
            if (mExecutionCount == 0)
            {
#if DEBUG_TREE
                if (mPlayer.MainLooper == this)
                    BehaviourTreePlayer.InvokeRecursive(mRoot, true, (node) => node.IsStateObsolute = true);
#endif
                mState = NodeState.Inactive;
                var node = mRoot;
                if (node != null)
                {
                    var stat = node.GetInterruptResult(mPlayer);
                    if (stat == NodeState.Running)
                    {
                        mState = NodeState.Running;
                        mExecutionBuffer[mExecutionCount++] = node;
                        for (int i = 0; i < mRootCallbacks.Length; i++)
                        {
                            mRootCallbacks[i].OnStart(mPlayer);
                        }
                        node.InvokeOnStart(mPlayer);
                    }
                    else
                    {
                        node.InvokeInterrupt(mPlayer, stat == NodeState.Success);
                        mState = node.State;
                        return;
                    }
                }
                else
                {
                    return;
                }
            }
            else
            {
                // update condition
                for (int i = 0; i < mExecutionCount; i++)
                {
                    var item = mExecutionBuffer[i];
                    var stat = item.GetInterruptResult(mPlayer);
                    if (stat != NodeState.Running)
                    {
                        var successInterruption = stat == NodeState.Success;
                        // interrupt
                        for (int lastIndex = mExecutionCount - 1; lastIndex >= i; lastIndex--)
                        {
                            var last = mExecutionBuffer[lastIndex];
                            last.InvokeInterrupt(mPlayer, successInterruption);
                            last.InvokeOnStop(mPlayer);
                        }
                        mExecutionCount = i;
                        if (mExecutionCount == 0)
                        {
                            mState = item.State;
                            for (int k = 0; k < mRootCallbacks.Length; k++)
                            {
                                mRootCallbacks[k].OnStop(mPlayer);
                            }
                            // 结束所有节点执行，强制中断一帧
                            return;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
            mSaftyCheck.Clear();
            var stopRoot = mExecutionCount > 0;
            while (mExecutionCount > 0)
            {
                var last = mExecutionBuffer[mExecutionCount - 1];
                if (last is ActionNode anode)
                {
                    anode.InvokeOnUpdate(mPlayer);
                    if (anode.State == NodeState.Running)
                    {
#if DEBUG_TREE
                        mPlayer.AddActiveNode(anode);
#endif
                        return;
                    }
                    mExecutionCount--;
                    anode.InvokeOnStop(mPlayer);
                }
                else if (last is ControllerNode cnode)
                {
                    var next = cnode.InvokeNextNode(mPlayer);
                    if (next == null)
                    {
                        if(cnode.IsLoopController && cnode.State == NodeState.Running)
                        {
                            ((ILoopController)cnode).OnResetLoop(mPlayer);
                            return;
                        }
                        mExecutionCount--;
                        cnode.InvokeOnStop(mPlayer);
                    }
                    else if (!mSaftyCheck.Add(next))
                    {
                        Debug.LogError($"The Controller<{cnode.GetType().FullName}> executes the same child multi-times in one loop and it will be stoped.");
                        mExecutionCount--;
                        cnode.InvokeOnStop(mPlayer);
                    }
                    else
                    {
                        var stat = next.GetInterruptResult(mPlayer);
                        if (stat != NodeState.Running)
                        {
                            next.InvokeInterrupt(mPlayer, stat == NodeState.Success);
                        }
                        else if (mExecutionCount < mExecutionBuffer.Length)
                        {
                            mExecutionBuffer[mExecutionCount++] = next;
                            next.InvokeOnStart(mPlayer);
                        }
                        else
                        {
                            Debug.LogError($"behaviour tree stack overflow on execute <{next.GetType().FullName}> (stack size: {mExecutionBuffer.Length})");
                            return;
                        }
                    }
                }
                else
                {
                    mExecutionCount--;
                    last.InvokeOnStop(mPlayer);
                }
            }
            if (mRoot != null)
            {
                mState = mRoot.State;
                if (stopRoot)
                {
                    for (int i = 0; i < mRootCallbacks.Length; i++)
                    {
                        mRootCallbacks[i].OnStop(mPlayer);
                    }
                }
            }
        }

    }
}
